import socket
from queue import Queue
import  time
import threading
import binascii

class TIce104MasterEvents:
    def __init__(self , _master) -> None:
        self.master = _master
    def onConnected(self):     
        try:
            pass
        except Exception as e:
            print(e)
    def onDisConnected(self):     
        try:
            pass
        except Exception as e:
            print(e)
    def onSendPgk(self , _pgk):     
        try:
            pass
        except Exception as e:
            print(e)
    def onRecPgk(self , iframeType, _pgk ):     
        try:
            pass
        except Exception as e:
            print(e)

class TIce104PgkAPCI:
    def __init__(self ) -> None:
        self.headFlag= 0x68
        self.l = 0x04
        self.c1 = 0x0
        self.c2 = 0x0
        self.c3 = 0x0
        self.c4 = 0x0

class TIce104PgkASDU:
    def __init__(self ) -> None:
        self.typ= 0x0 #类型标识
        self.vsq= 0x0 #可变结构限定词
        self.cot= 0x0 #传送原因 2 byte
        self.adr= 0x0 #ASDU公共地址 2 byte
        self.infoAdr= 0x0 #信息对象地址 3 byte
        self.infoBody = bytearray([])

tabBodyInfoConfig=[
    {"typ":"1" , "lab":"" , "q":1 , "t":0 , "l":1} ,
    {"typ":"3" , "lab":"" , "q":1 , "t":0 , "l":1} ,
    {"typ":"5" , "lab":"" , "q":1 , "t":0 , "l":1} ,
    {"typ":"7" , "lab":"" , "q":1 , "t":0 , "l":4} ,
    {"typ":"9" , "lab":"" , "q":1 , "t":0 , "l":2} ,
    {"typ":"11" , "lab":"" , "q":1 , "t":0 , "l":2} ,
    {"typ":"13" , "lab":"" , "q":1 , "t":0 , "l":4} ,
    {"typ":"15" , "lab":"" , "q":1 , "t":0 , "l":4} ,#累计量，4字节？
    {"typ":"20" , "lab":"" , "q":0 , "t":0 , "l":1} ,
    {"typ":"21" , "lab":"" , "q":0 , "t":0 , "l":2} ,
    {"typ":"30" , "lab":"" , "q":1 , "t":1 , "l":1} ,
    {"typ":"31" , "lab":"" , "q":1 , "t":1 , "l":1} ,
    {"typ":"32" , "lab":"" , "q":1 , "t":1 , "l":1} ,
    {"typ":"33" , "lab":"" , "q":1 , "t":1 , "l":4} ,
    {"typ":"34" , "lab":"" , "q":1 , "t":1 , "l":2} ,
    {"typ":"35" , "lab":"" , "q":1 , "t":1 , "l":2} ,
    {"typ":"36" , "lab":"" , "q":1 , "t":1 , "l":4} ,
    {"typ":"37" , "lab":"" , "q":1 , "t":1 , "l":4} ,#累计量，4字节？
    {"typ":"38" , "lab":"" , "q":1 , "t":1 , "l":1} ,
    {"typ":"39" , "lab":"" , "q":1 , "t":1 , "l":1} ,
    {"typ":"40" , "lab":"" , "q":1 , "t":1 , "l":1} 
]
class TIce104DataItem:
    def __init__(self) -> None:
        self.addr=0
        self.code =0 
        self.val =0
        self.q =0
        self.tm = 0
        self.tmY=0
        self.tmM=0
        self.tmD=0
        self.tmH=0
        self.tmMi=0
        self.tmS=0
        self.tmMs=0
class TIce104Pgk:
    def __init__(self ) -> None:
        self.frameType = "U"
        self.apci = TIce104PgkAPCI()
        self.asdu = TIce104PgkASDU()
        self.frameType = "U"
    @staticmethod
    def decodePgk(self , bytes): 
        retPgk = TIce104Pgk()
        try:
            pass
        except Exception as e:
            print(e) 
        return retPgk
    @staticmethod
    def decodeIframeInfoBody(self , asdu): 
        retItems = []
        try:
            index =0
            
        except Exception as e:
            print(e) 
        return retItems

    def assign(self):
        try:
            if self.apci.c1==1 and self.apci.c2 == 0 :
                self.frameType="S"
            elif self.apci.c1 & 0x03 == 3 :
                self.frameType ="U"
            else :
                self.frameType = "I"
                if len(self.asdu.infoBody)>0 :
                    pass
            #print( self.frameType +"  frame.")
        except Exception as er:
            print(er)

class TIce104Master:
    def __init__(self) -> None:
        self.skt = None
        self.host = "192.169.100.20"
        self.port = 2404
        self.rxCounter = 0
        self.txCounter = 0
        self.inQueue = Queue()
        self.outQueue = Queue()
        self.events = TIce104MasterEvents(self)
        self.lastRecPgkTime =0
        self.lastCallTotalTime =0

        self.isConnected = False
        self.isActive = False
        self.isTestLink = False
        self.lastTestLinkTime = 0
        self.testLinkCycle = 8
        self.isCallTotal = False
        self.lastCallTotalTime =0
        self.callTotalCycle = 60
    def acConnect(self):
        try:
            self.isCallTotal = False
            self.isActive = False
            self.isTestLink = False
            self.isCallTotal = False
            self.skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server_address = (self.host , self.port)
            try:
                self.skt.connect(server_address)
                self.isConnected = True
                print('connect slave success.')
            except Exception as ee:
                print('connect slave faild.')
                print(ee)
        except Exception as er:
            print('acConnect error' , er)
    def acRecPgk(self):
        status= False
        pgk = TIce104Pgk()
        try:
            headFlagBytes = self.skt.recv(1)
            if len(headFlagBytes)==1 and headFlagBytes[0]==0x68:
                headBytes = self.skt.recv(1)
                size = headBytes[0]
                pgkBytes = self.skt.recv(size)
                pgk.apci.l = size
                pgk.apci.c1 = pgkBytes[0]
                pgk.apci.c2 = pgkBytes[1]
                pgk.apci.c3 = pgkBytes[2]
                pgk.apci.c4 = pgkBytes[3]
                tmp=[0x68 , size ]
                for i in pgkBytes:
                    tmp.append(i)

                self.log('r<',bytes(tmp) )
                if size >4:
                    bodyBytes = pgkBytes[4:]
                    offset = 0
                    pgk.asdu.typ = bodyBytes[offset]
                    offset = offset +1
                    pgk.asdu.vsq = bodyBytes[offset]
                    offset = offset +1
                    cotBytes = bodyBytes[offset:offset+2]
                    pgk.asdu.cot = int.from_bytes(cotBytes, 'little', signed=False)
                    offset = offset +2
                    adrBytes = bodyBytes[offset:offset+2]
                    pgk.asdu.adr = int.from_bytes(adrBytes , 'little' , signed=False)
                    offset = offset +2
                    pgk.asdu.infoBody = bodyBytes[offset:]
                pgk.assign()
                if pgk.frameType=="I":
                    self.rxCounter = int.from_bytes(bytearray([pgk.apci.c1,pgk.apci.c2]), 'little', signed=False)
                status = True
        except Exception as er:
            print('acRecPgk error' , er)
        return  ( status , pgk)
    def acActiveLink(self):
        ret=0
        try:
            self.isActive = False
            activePgk = bytearray([0x68 , 0x04 , 0x07 , 0x00 , 0x00 , 0x00 ])
            self.skt.send(activePgk)
            self.log('s>', activePgk)
            status , pgk = self.acRecPgk()
            if status and pgk.apci.c1 ==0x0B:
                time.sleep(2)
                status, pgk = self.acRecPgk()
                if status and pgk.asdu.typ == 0x46 :
                    self.lastCallTotalTime = 0
                    self.lastTestLinkTime = time.time()
                    self.isActive = True
                    ret =1
        except Exception as er:
            print(er)
        return ret
    def acSendSFrame(self):
        try:
            c3 = self.rxCounter & 0xFE
            c4 = (self.rxCounter >> 8) & 0xFF
            pgkBytes = bytearray([0x68 , 0x04 , 0x01 , 0x00 , c3 , c4 , 13])
            self.skt.send(pgkBytes)
            self.log('s>', pgkBytes)
        except Exception as er:
            print(er)
    def acCallTotal(self):
        try:
            nowTm = time.time()
            dltTm = nowTm - self.lastCallTotalTime
            if dltTm>=self.callTotalCycle:
                self.lastCallTotalTime = nowTm
                c1 = self.txCounter & 0xFE
                c2 = (self.txCounter >> 8) & 0xFF
                c3 = self.rxCounter & 0xFE
                c4 = (self.rxCounter >> 8 ) & 0xFF
                pgkBytes = bytearray([0x68 , 0x0E , c1 , c2 , c3 , c4 , 0x64 , 0x01 , 0x06 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x14 , 13 ])
                self.skt.send(pgkBytes)
                self.log('s>', pgkBytes)
                self.txCounter = self.txCounter + 2
            threading.Timer(2 , self.acCallTotal).start()
        except Exception as er:
            print(er)
    def acTestLink(self):
        try:
            nowTm = time.time()
            dltTm = nowTm - self.lastTestLinkTime
            if self.lastTestLinkTime>0 and  dltTm>=self.testLinkCycle:
                self.lastTestLinkTime = nowTm
                pgkBytes = bytearray([0x68 , 0x04 , 0x43 , 0x00  , 0x00  , 0x00 , 13])
                self.skt.send(pgkBytes)
                self.log('s>', pgkBytes)
            threading.Timer(1 , self.acTestLink).start()
        except Exception as er:
            print(er)
    def open(self):
        try:
            self.acConnect()
            if self.isConnected:
                self.acActiveLink()
                self.lastCallTotalTime = 0
                self.lastTestLinkTime = time.time()

                if self.isActive:

                    thread_recPgk = threading.Thread(target=self.threadExe_recPgk)
                    thread_recPgk.setDaemon(True)
                    thread_recPgk.start()

                    self.acCallTotal()
                    self.acTestLink()


        except Exception as er:
            print(er)

    def threadExe_recPgk(self):
        try:
            while self.isActive :
                status , pgk = self.acRecPgk()
                if status :
                    self.acSendSFrame()
                time.sleep(1)
        except Exception as er:
            print('threadExe_recPgk error' , er)
    def log(self , txt , bytes):
        res = txt
        for b in bytes:
            res = res +" "+ ((str(hex(b)))[2:]).zfill(2)
        print(res)

class TIce104Master111_:
    def __init__(self) -> None:
        self.skt = None
        self.host = "192.169.100.20"
        self.port = 2404
        self.rxCounter = 0 
        self.txCounter = 0 
        self.inQueue = Queue()
        self.outQueue = Queue()
        self.events = TIce104MasterEvents(self)
        self.lastRecPgkTime =0
        self.lastCallTotalTime =0

        self.isConnected = False
        self.isActive = False
        self.isTestLink = False
        self.lastTestLinkTime = 0
        self.testLinkCycle = 8
        self.isCallTotal = False
        self.lastCallTotalTime =0
        self.callTotalCycle = 60
    def acConnect(self):
        try:
            self.isCallTotal = False
            self.isActive = False
            self.isTestLink = False
            self.isCallTotal = False
            self.skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            #self.skt.settimeout(2)
            #self.skt.bind(('192.169.100.30', ))
            server_address = (self.host , self.port)
            try:
                #self.skt.settimeout(5)
                self.skt.connect(server_address)
                self.isConnected = True
                print('connect slave success.')
            except Exception as ee:
                print('connect slave faild.')
                print(ee)
        except Exception as er:
            print('acConnect error' , er)
    def acRecPgk(self):
        status= False
        pgk = TIce104Pgk()
        try:
            headFlagBytes = self.skt.recv(1)
            if len(headFlagBytes)==1 and headFlagBytes[0]==0x68:
                headBytes = self.skt.recv(5)
                bodysize  = headBytes[0]-4
                bodyBytes= bytes([])
                pgk.apci.l = bodysize
                pgk.apci.c1 = headBytes[1]
                pgk.apci.c2 = headBytes[2]
                pgk.apci.c3 = headBytes[3]
                pgk.apci.c4 = headBytes[4]
                self.log('r<',bytes([0x68,bodysize+4 , pgk.apci.c1 , pgk.apci.c2 , pgk.apci.c3 , pgk.apci.c4]))
                if bodysize >0:
                    bodyBytes = bytes(self.skt.recv(bodysize))
                    self.log('r<-', bodyBytes)
                    offset = 0
                    pgk.asdu.typ = bodyBytes[offset]
                    offset = offset +1
                    pgk.asdu.vsq = bodyBytes[offset]
                    offset = offset +1
                    cotBytes = bodyBytes[offset:offset+2]
                    pgk.asdu.cot = int.from_bytes(cotBytes, 'little', signed=False)
                    offset = offset +2
                    adrBytes = bodyBytes[offset:offset+2]
                    pgk.asdu.adr = int.from_bytes(adrBytes , 'little' , signed=False)
                    offset = offset +2
                    pgk.asdu.infoBody = bodyBytes[offset:]
                pgk.assign()
                if pgk.frameType=="I":
                    self.rxCounter = int.from_bytes(bytearray([pgk.apci.c1,pgk.apci.c2]), 'little', signed=False)
                status = True
        except Exception as er:
            print('acRecPgk error' , er)
        return  ( status , pgk)
    def acActiveLink(self):
        ret=0
        try:
            self.isActive = False
            activePgk = bytearray([0x68 , 0x04 , 0x07 , 0x00 , 0x00 , 0x00 ])
            self.skt.send(activePgk)
            self.log('s>', activePgk)
            status , pgk = self.acRecPgk()
            if status and pgk.apci.c1 ==0x0B:
                time.sleep(2)
                status, pgk = self.acRecPgk()
                if status and pgk.asdu.typ == 0x46 :
                    self.lastCallTotalTime = 0
                    self.lastTestLinkTime = time.time()
                    self.isActive = True
                    ret =1
        except Exception as er:
            print(er)
        return ret
    def acCallTotal(self):
        try:
            nowTm = time.time()
            dltTm = nowTm - self.lastCallTotalTime
            if dltTm>=self.callTotalCycle:
                self.lastCallTotalTime = nowTm
                c1 = self.txCounter & 0xFE
                c2 = (self.txCounter >> 8) & 0xFF
                c3 = self.rxCounter & 0xFE
                c4 = (self.rxCounter >> 8 ) & 0xFF
                pgkBytes = bytearray([0x68 , 0x0E , c1 , c2 , c3 , c4 , 0x64 , 0x01 , 0x06 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x14 , 13 ])
                self.skt.send(pgkBytes)
                self.log('s>', pgkBytes)
                self.txCounter = self.txCounter + 2
                isRecCallTotalPgks = True
                while isRecCallTotalPgks:
                    status , pgk = self.acRecPgk()
                    if status== 0:
                        isRecCallTotalPgks = False
                    else:
                        self.acSendSFrame()
                        if pgk.asdu.cot == 0x0A:
                            isRecCallTotalPgks = False
        except Exception as er:
            print(er)
    def acSendSFrame(self):
        try:
            c3 = self.rxCounter & 0xFE
            c4 = (self.rxCounter >> 8) & 0xFF
            pgkBytes = bytearray([0x68 , 0x04 , 0x01 , 0x00 , c3 , c4 , 13])
            self.skt.send(pgkBytes)
            self.log('s>', pgkBytes)
        except Exception as er:
            print(er)
    def acTestLink(self):
        try:
            nowTm = time.time()
            dltTm = nowTm - self.lastTestLinkTime
            if self.lastTestLinkTime>0 and  dltTm>=self.testLinkCycle:
                self.lastTestLinkTime = nowTm
                pgkBytes = bytearray([0x68 , 0x04 , 0x43 , 0x00  , 0x00  , 0x00 , 13])
                self.skt.send(pgkBytes)
                self.log('s>', pgkBytes)
                status, pgk = self.acRecPgk()
                self.acSendSFrame()

        except Exception as er:
            print(er)
    def acRespTestLink(self , c1):
        try:
            c = 0x00
            if  c1 == 0x07 :
                c = 0x0B
            if  c1 == 0x13 :
                c = 0x23
            if  c1 == 0x43 :
                c = 0x83
            pgkBytes = bytearray([0x68 , 0x04 , c , 0x00  , 0x00  , 0x00  ])
            self.skt.send(pgkBytes)
            self.log('s>', pgkBytes)
        except Exception as er:
            print(er)

    def open(self):
        try:
            self.acConnect()
            if self.isConnected:
                self.acActiveLink()
                self.lastCallTotalTime = 0
                self.lastTestLinkTime = time.time()
                while self.isActive:
                    try:
                        self.acCallTotal()
                        self.acTestLink()
                        time.sleep(1)
                    except Exception as ee:
                        print(ee)
                        self.isActive = False
                        self.isConnected = False

        except Exception as er:
            print(er)

    def start(self):
        thread_run = threading.Thread(target=self.open)
        thread_run.setDaemon(True)
        thread_run.start()
    def open_back(self):
        try:
            self.acConnect()
            if self.isConnected:
                self.acActiveLink()
                while self.isActive:
                    try:
                        self.acCallTotal()
                        self.acTestLink()
                        status, pgk = self.acRecPgk()
                        if status:
                            if pgk.asdu.typ in [0x64] and pgk.asdu.cot == 0x0A:
                                self.acSendSFrame()
                        else:
                            time.sleep(1)
                            self.acTestLink()
                    except Exception as ee:
                        print(ee)
                        self.isActive = False
                        self.isConnected = False

        except Exception as er:
            print(er)
    def logSend(self , txt , bytes):
        res = 'send ' + txt
        for b in bytes:
            res = res +" "+ ((str(hex(b)))[2:]).zfill(2)
        print(res)
    def log(self , txt , bytes):
        res = txt
        for b in bytes:
            res = res +" "+ ((str(hex(b)))[2:]).zfill(2)
        print(res)


    




